//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:  
    blcommon.c
    
Abstract:  
    Bootloader common main module. This file contains the C BootloaderMain
    function for the boot loader.    NOTE: The firmware "entry" point (the real
    entry point) is _StartUp in init assembler file.

    The Windows CE boot loader is the code that is executed on a Windows CE
    development system at power-on reset and loads the Windows CE
    operating system. The boot loader also provides code that monitors
    the behavior of a Windows CE platform between the time the boot loader
    starts running and the time the full operating system debugger is 
    available. Windows CE OEMs are supplied with sample boot loader code 
    that runs on a particular development platform and CPU.
    
Functions:


Notes: 

--*/
#include <windows.h>
#include <halether.h>
#include <blcommon.h>
#include "loader.h"
#include <fmd.h>
#include <flash_cfg.h>

#define SPIN_FOREVER        while (1)

ROMHDR * volatile const pTOC = (ROMHDR *)-1;     // Gets replaced by RomLoader with real address
static DownloadManifest g_DownloadManifest;
static BOOL g_bBINDownload = TRUE;

static BOOL KernelRelocate (ROMHDR *const pTOC);
static BOOL DownloadImage (LPDWORD pdwImageStart, LPDWORD pdwImageLength, LPDWORD pdwLaunchAddr);
static BOOL DownloadImageSDMMC (LPDWORD pdwImageStart, LPDWORD pdwImageLength, LPDWORD pdwLaunchAddr);
static BOOL IsKernelRegion(DWORD dwRegionStart, DWORD dwRegionLength);
static VOID DumpTOC(ROMHDR *pTOC);
void LoadImageDDR(BOOL enableJump);

#define CURRENT_VERSION_MAJOR       1
#define CURRENT_VERSION_MINOR       1

const unsigned char NKSignon[] = {
    "\nMicrosoft Windows CE Ethernet Bootloader Common Library Version %d.f%d Built "
    __DATE__ " " __TIME__ "\r\n"
};

DWORD g_dwROMOffset;
extern FlashInfo g_FlashInfo;

PFN_OEMVERIFYMEMORY     g_pOEMVerifyMemory;
PFN_OEMREPORTERROR      g_pOEMReportError;
PFN_OEMCHECKSIGNATURE   g_pOEMCheckSignature;
PFN_OEMMULTIBINNOTIFY   g_pOEMMultiBINNotify;

void RunTests(void);


static void HALT (DWORD dwReason)
{
    if (g_pOEMReportError)
    {
        g_pOEMReportError (dwReason, 0);
    }
    SPIN_FOREVER;
}


void BootloaderMain (void)
{
    DWORD dwAction;   
    DWORD dwpToc = 0;
    DWORD dwImageStart = 0, dwImageLength = 0, dwLaunchAddr = 0;
    BOOL bDownloaded = FALSE;
	UINT8 * pImageBufferRam = 0;
	UINT16 numBlocks = 0;
	BOOL   status;
	FlashInfo flashInfo;

    // relocate globals to RAM
    if (!KernelRelocate (pTOC))
    {
        // spin forever
        HALT (BLERR_KERNELRELOCATE);
    }

    
    // (1) Init debug support. We can use OEMWriteDebugString afterward.
    if (!OEMDebugInit ())
    {
        // spin forever
        HALT (BLERR_DBGINIT);
    }

    // output banner
    EdbgOutputDebugString (NKSignon, CURRENT_VERSION_MAJOR, CURRENT_VERSION_MINOR);

    // (3) initialize platform (clock, drivers, transports, etc)
    if (!OEMPlatformInit ())
    {
        // spin forever
        HALT (BLERR_PLATINIT);
    }

    // system ready, preparing for download
    EdbgOutputDebugString ("System ready!\r\nPreparing for download...\r\n");

    // (4) call OEM specific pre-download function
    switch (dwAction = OEMPreDownload ())
    {
    case BL_DOWNLOAD:

	    /* Before Download image, it needs to update the reserve bit of flash blocks */
	    EdbgOutputDebugString("Before download image, it needs to update reserved spare area of flash.\n");

	    if (g_FlashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE)
	        ReservedSpareArea(0, LB_FLASH_BLOCK_EBOOTCFG_START + 1);
	    else
	        ReservedSpareArea(0, FLASH_BLOCK_EBOOTCFG_START + 1);

        // (5) download image
         //EdbgOutputDebugString ("before DownloadImage\r\n");
        if (!DownloadImage (&dwImageStart, &dwImageLength, &dwLaunchAddr))
        {
        	         EdbgOutputDebugString ("Error\r\n");
            // error already reported in DownloadImage
            SPIN_FOREVER;
        }

        //EdbgOutputDebugString ("after DownloadImage\r\n");
		
        bDownloaded = TRUE;

        // Check for pTOC signature ("CECE") here, after image in place
        if (*(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
        {
            dwpToc = *(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG));
            // need to map the content again since the pointer is going to be in a fixup address
            dwpToc = (DWORD) OEMMapMemAddr (dwImageStart, dwpToc + g_dwROMOffset);

            EdbgOutputDebugString ("ROMHDR at Address %Xh\r\n", dwImageStart + ROM_SIGNATURE_OFFSET + sizeof (DWORD)); // right after signature
        }

	 /// Don't load image from flash. 
	  if (g_bBINDownload && g_pOEMCheckSignature)
        {
            if (!g_pOEMCheckSignature(dwImageStart, g_dwROMOffset, dwLaunchAddr, bDownloaded))
                HALT(BLERR_CAT_SIGNATURE);
        }

	  // Launch kernal
	  OEMLaunch ((DWORD)OEMMapMemAddr(dwImageStart,dwImageStart) ,
	  			dwImageLength, 
	  			(DWORD)OEMMapMemAddr(dwLaunchAddr,dwLaunchAddr) ,
	  			(const ROMHDR *)dwpToc);
	 //never return;
	  HALT (BLERR_INVALIDCMD);

    case BL_SDMMC_DOWNLOAD:
    //     EdbgOutputDebugString ("before DownloadImage\r\n");
        if (!DownloadImageSDMMC (&dwImageStart, &dwImageLength, &dwLaunchAddr))
        {
        	         EdbgOutputDebugString ("Error\r\n");
            // error already reported in DownloadImage
            SPIN_FOREVER;
        }

      //  EdbgOutputDebugString ("after DownloadImage\r\n");
		
        bDownloaded = TRUE;

        // Check for pTOC signature ("CECE") here, after image in place
        if (*(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
        {
            dwpToc = *(LPDWORD) OEMMapMemAddr (dwImageStart, dwImageStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG));
            // need to map the content again since the pointer is going to be in a fixup address
            dwpToc = (DWORD) OEMMapMemAddr (dwImageStart, dwpToc + g_dwROMOffset);

            EdbgOutputDebugString ("ROMHDR at Address %Xh\r\n", dwImageStart + ROM_SIGNATURE_OFFSET + sizeof (DWORD)); // right after signature
        }

	 /// Don't load image from flash. 
	  if (g_bBINDownload && g_pOEMCheckSignature)
        {
            if (!g_pOEMCheckSignature(dwImageStart, g_dwROMOffset, dwLaunchAddr, bDownloaded))
                HALT(BLERR_CAT_SIGNATURE);
        }

	  // Launch kernal
	  OEMLaunch ((DWORD)OEMMapMemAddr(dwImageStart,dwImageStart) ,
	  			dwImageLength, 
	  			(DWORD)OEMMapMemAddr(dwLaunchAddr,dwLaunchAddr) ,
	  			(const ROMHDR *)dwpToc);
	 //never return;
	  HALT (BLERR_INVALIDCMD);
	  
        // fall through
    case BL_JUMP:
        if (dwAction != BL_DOWNLOAD && dwAction != BL_SDMMC_DOWNLOAD) // Only load from flash if we didn't download
        {
            LoadImageDDR(TRUE);

            // Before jumping to the image, optionally check the image signature.
            // NOTE: if we haven't downloaded the image by now, we assume that it'll be loaded from local storage in OEMLaunch (or it
            // already resides in RAM from an earlier download), and in this case, the image start address might be 0.  This means 
            // that the image signature routine will need to find the image in storage or in RAM to validate it.  Since the OEM"s 
            // OEMLaunch function will need to do this anyways, we trust that it's within their abilities to do it here.
            //

            // Get image start address in RAM
            pImageBufferRam = (UINT8 *)OS_RAM_IMAGE_START;

			//Get flash info
			if (FMD_GetInfo(&flashInfo) == FALSE) 
			{
				EdbgOutputDebugString("ERROR: BootloaderMain: FMD_GetInfo call failed!\r\n");
    			HALT(BLERR_INVALIDCMD);
			}

			// Load OS Image from flash
                     if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE)
			{
				// Get Image size
				numBlocks = (UINT16) LB_OS_RAM_IMAGE_SIZE_NUM_BLOCKS;
				status = LoadImageFromFlash((BLOCK_ID)LB_FLASH_BLOCK_OS_START, pImageBufferRam, numBlocks);
			}
				
			else
			{
            // Get Image size
            numBlocks = (UINT16)OS_RAM_IMAGE_SIZE_NUM_BLOCKS;
				status = LoadImageFromFlash((BLOCK_ID)FLASH_BLOCK_OS_START, pImageBufferRam, numBlocks);
			}

			EdbgOutputDebugString ("BootloaderMain: numBlocks = %d\r\n", numBlocks);

			if (!status)
            {
                EdbgOutputDebugString ("'Loading OS Image from flash failed...\r\n");
                // ERROR! spin forever
                HALT (BLERR_INVALIDCMD);
            }
            
        }

		// Check image signature
        if (g_bBINDownload && g_pOEMCheckSignature)
        {
            if (!g_pOEMCheckSignature(dwImageStart, g_dwROMOffset, dwLaunchAddr, bDownloaded))
                HALT(BLERR_CAT_SIGNATURE);
        }
        // (5) final call to launch the image. never returned
        OEMLaunch (dwImageStart, dwImageLength, dwLaunchAddr, (const ROMHDR *)dwpToc);
        // should never return
        // fall through
    default:
        // ERROR! spin forever
        HALT (BLERR_INVALIDCMD);
    }
}


//
// KernelRelocate: move global variables to RAM
//
static BOOL KernelRelocate (ROMHDR *const pTOC)
{
    ULONG loop;
    COPYentry *cptr;
    if (pTOC == (ROMHDR *const) -1)
    {
        return (FALSE); // spin forever!
    }
    // This is where the data sections become valid... don't read globals until after this
    for (loop = 0; loop < pTOC->ulCopyEntries; loop++)
    {
        cptr = (COPYentry *)(pTOC->ulCopyOffset + loop*sizeof(COPYentry));
        if (cptr->ulCopyLen)
            memcpy((LPVOID)cptr->ulDest,(LPVOID)cptr->ulSource,cptr->ulCopyLen);
        if (cptr->ulCopyLen != cptr->ulDestLen)
            memset((LPVOID)(cptr->ulDest+cptr->ulCopyLen),0,cptr->ulDestLen-cptr->ulCopyLen);
    }
    
    DumpTOC(pTOC);

    return (TRUE);
}

static BOOL VerifyChecksum (DWORD cbRecord, LPBYTE pbRecord, DWORD dwChksum)
{
    // Check the CRC
    DWORD dwCRC = 0;
    DWORD i;
    for (i = 0; i < cbRecord; i++)
        dwCRC += *pbRecord ++;

    if (dwCRC != dwChksum)
        EdbgOutputDebugString ("ERROR: Checksum failure (expected=0x%x  computed=0x%x)\r\n", dwChksum, dwCRC);

    return (dwCRC == dwChksum);
}

#define BL_HDRSIG_SIZE		7
static BOOL DownloadImage (LPDWORD pdwImageStart, LPDWORD pdwImageLength, LPDWORD pdwLaunchAddr)
{
    BYTE hdr[BL_HDRSIG_SIZE];
    DWORD dwRecLen, dwRecChk, dwRecAddr;
    BOOL fIsFlash = FALSE;
    LPBYTE lpDest = NULL;
    int nPkgNum = 0;
    BYTE nNumDownloadFiles = 1;
    DWORD dwImageStart = 0;
    DWORD dwImageLength = 0;
    RegionInfo *pCurDownloadFile;

    *pdwImageStart = *pdwImageLength = *pdwLaunchAddr = 0;

    do
    {
        // read the 7 byte "magic number"
        //
        if (!OEMReadData (BL_HDRSIG_SIZE, hdr))
        {
            EdbgOutputDebugString ("\r\nUnable to read image signature.\r\n");
            HALT (BLERR_MAGIC);
            return (FALSE);
        }

        // An N000FF packet is manufactured by Platform Builder when we're
        // downloading multiple files or when we're downloading a .nb0 file.
        //
        if (!memcmp (hdr, "N000FF\x0A", BL_HDRSIG_SIZE))
        {
            // read the packet checksum.
            //
            if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk))
            {
                EdbgOutputDebugString("\r\nUnable to read download manifest checksum.\r\n");
                HALT (BLERR_MAGIC);
                return (FALSE);
            }

            // read BIN region descriptions (start address and length).
            //
            if (!OEMReadData (sizeof (DWORD), (LPBYTE) &g_DownloadManifest.dwNumRegions) ||
                !OEMReadData ((g_DownloadManifest.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_DownloadManifest.Region[0]))
            {
                EdbgOutputDebugString("\r\nUnable to read download manifest information.\r\n");
                HALT (BLERR_MAGIC);
                return (FALSE);
            }

            // verify the packet checksum.
            //
            if (!VerifyChecksum((g_DownloadManifest.dwNumRegions * sizeof(RegionInfo)), (LPBYTE) &g_DownloadManifest.Region[0], dwRecChk))
            {
                EdbgOutputDebugString ("\r\nDownload manifest packet failed checksum verification.\r\n");
                HALT (BLERR_CHECKSUM);
                return (FALSE);
            }

            // Provide the download manifest to the OEM.  This gives the OEM the
            // opportunity to provide start addresses for the .nb0 files (which 
            // don't contain placement information like .bin files do).
            //
            if (g_pOEMMultiBINNotify)
            {
                g_pOEMMultiBINNotify((PDownloadManifest)&g_DownloadManifest);
            }

            // look for next download...
            nNumDownloadFiles = (BYTE)(g_DownloadManifest.dwNumRegions + 1);      // +1 to account for this packet.
            continue;
        }
        // Is this an old X000FF multi-bin packet header?  It's no longer supported.
        //
        else if (!memcmp (hdr, "X000FF\x0A", BL_HDRSIG_SIZE))
        {
            EdbgOutputDebugString ("ERROR: The X000FF packet is an old-style multi-bin download manifest and it's no longer supported. \
                                   \r\nPlease update your Platform Builder installation in you want to download multiple files.\r\n");
            HALT (BLERR_MAGIC);
            return (FALSE);
        }
        // Is this a standard bin image?  Check for the usual bin file signature.
        //
        else if (!memcmp (hdr, "B000FF\x0A", BL_HDRSIG_SIZE))
        {
            g_bBINDownload = TRUE;

            if (!OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageStart)
                || !OEMReadData (sizeof (DWORD), (LPBYTE) &dwImageLength))
            {
                EdbgOutputDebugString ("Unable to read image start/length\r\n");
                HALT (BLERR_MAGIC);
                return (FALSE);
            }
        }
        // If the header signature isn't recognized, we'll assume the
        // download file is a raw .nb0 file.
        //
        else
        {
            g_bBINDownload = FALSE;
        }

        // If Platform Builder didn't provide a download manifest (i.e., we're
        // only downloading a single .bin file), manufacture a manifest so we
        // can notify the OEM.
        //
        if (!g_DownloadManifest.dwNumRegions)
        {
            g_DownloadManifest.dwNumRegions             = 1;
            g_DownloadManifest.Region[0].dwRegionStart  = dwImageStart;
            g_DownloadManifest.Region[0].dwRegionLength = dwImageLength;

            // Provide the download manifest to the OEM.
            //
            if (g_pOEMMultiBINNotify)
            {
                g_pOEMMultiBINNotify((PDownloadManifest)&g_DownloadManifest);
            }
        }

        // Locate the current download manifest entry (current download file).
        //
        pCurDownloadFile = &g_DownloadManifest.Region[g_DownloadManifest.dwNumRegions - nNumDownloadFiles];

        // give the OEM a chance to verify memory
        if (g_pOEMVerifyMemory && !g_pOEMVerifyMemory (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
        {
            EdbgOutputDebugString ("!OEMVERIFYMEMORY: Invalid image\r\n");
            HALT (BLERR_OEMVERIFY);
            return (FALSE);
        }

        // check for flash image. Start erasing if it is.
        if ((fIsFlash = OEMIsFlashAddr (pCurDownloadFile->dwRegionStart)) 
            && !OEMStartEraseFlash (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
        {
            EdbgOutputDebugString ("Invalid Flash Address/Length\r\n");
            HALT (BLERR_FLASHADDR);
            return (FALSE);
        }

        // if we're downloading a binary file, we've already downloaded part of the image when searching
        // for a file header.  copy what we've read so far to the destination buffer, then finish downloading.
        if (!g_bBINDownload)
        {

            lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart);
            memcpy(lpDest, hdr, BL_HDRSIG_SIZE);

            // complete the file download...
            // read data block
            if (!OEMReadData ((pCurDownloadFile->dwRegionLength - BL_HDRSIG_SIZE), (lpDest + BL_HDRSIG_SIZE)))
            {
                EdbgOutputDebugString ("ERROR: failed when reading raw binary file.\r\n");
                HALT (BLERR_CORRUPTED_DATA);
                return (FALSE);
            }
        }
        // we're downloading a .bin file - download each .bin record in turn...
        else
        {
			EdbgOutputDebugString("DownloadImage: Binary file\r\n");
            while (OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecAddr) &&
                   OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecLen)  &&
                   OEMReadData (sizeof (DWORD), (LPBYTE) &dwRecChk))
            {
                //EdbgOutputDebugString("Record: Addr=0x%X, Len=0x%X (%d), Chk=0x%X\r\n", dwRecAddr, dwRecLen, dwRecLen, dwRecChk);
                // last record of .bin file uses sentinel values for address and checksum.
                if (!dwRecAddr && !dwRecChk)
                {
					EdbgOutputDebugString("Download Completed\r\n");
                    break;
                }

                // map the record address (FLASH data is cached, for example)
                lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, dwRecAddr);

		//EdbgOutputDebugString("Download to 0x%x\r\n", lpDest);

                // read data block
                if (!OEMReadData (dwRecLen, lpDest))
                {
                    EdbgOutputDebugString ("****** Data record %d corrupted, ABORT!!! ******\r\n", nPkgNum);
                    HALT (BLERR_CORRUPTED_DATA);
                    return (FALSE);
                }

                if (!VerifyChecksum (dwRecLen, lpDest, dwRecChk))
                {
                    EdbgOutputDebugString ("****** Checksum failure on record %d, ABORT!!! ******\r\n", nPkgNum);
                    HALT (BLERR_CHECKSUM);
                    return (FALSE);
                }

                // Look for ROMHDR to compute ROM offset.  NOTE: romimage guarantees that the record containing
                // the TOC signature and pointer will always come before the record that contains the ROMHDR contents.
                //
                if (dwRecLen == sizeof(ROMHDR) && (*(LPDWORD) OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE))
                {
                    DWORD dwTempOffset = (dwRecAddr - *(LPDWORD)OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG)));
                    ROMHDR *pROMHdr = (ROMHDR *)lpDest;

					EdbgOutputDebugString("*** Potential ROMHDR at %x\r\n", lpDest);
                    // Check to make sure this record really contains the ROMHDR.
                    //
                    if ((pROMHdr->physfirst == (pCurDownloadFile->dwRegionStart - dwTempOffset)) &&
                        (pROMHdr->physlast  == (pCurDownloadFile->dwRegionStart - dwTempOffset + pCurDownloadFile->dwRegionLength)) &&
                        (DWORD)(HIWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast) &&
                        (DWORD)(LOWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast))
                    {
                        g_dwROMOffset = dwTempOffset;
                        EdbgOutputDebugString("rom_offset=0x%x.\r\n", g_dwROMOffset); 
                    }
                }

                // verify partial checksum
                OEMShowProgress (nPkgNum ++);

                if (fIsFlash)
                {
                    OEMContinueEraseFlash ();
                }
            }
        }

		//EdbgOutputDebugString("Process data\r\n");
        // The image start address and length are passed back to the OEM code (OEMLaunch) 
        // in the following circumstances:
        // 1. The file is a raw .nb0 file.
        // 2. The file is a .bin file with a TOC that contains the kernel executable.
        // 3. The file is a .bin file without a TOC.
        //
        // If the image is a .bin file with a TOC that doesn't contain the kernel exectuable,
        // then it's a multi-xip/mulit-bin image for a non-kernel region and we don't pass
        // the start address and length back to the OEM code.  OEMLaunch can then save the
        // start address and length with the assurance that if the values are non-zero, they
        // represent the values for the NK region.
        //
        if (g_bBINDownload)
        {
			//EdbgOutputDebugString("Binary information: RegionStart=0x%x, RegionLength=0x%x\r\n", pCurDownloadFile->dwRegionStart,
			//	pCurDownloadFile->dwRegionLength);
			//EdbgOutputDebugString("Suppose now access violation\r\n");
			//EdbgOutputDebugString("Accessing data = 0x%x\r\n", *(UINT32 *)(pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET));
            // Does this .bin file contain a TOC?
            if (*(LPDWORD) OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
            {	
				//EdbgOutputDebugString("Access OKAY\r\n");
                // Contain the kernel?
                if (IsKernelRegion(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
                {
                    *pdwImageStart  = pCurDownloadFile->dwRegionStart;
                    *pdwImageLength = pCurDownloadFile->dwRegionLength;
                    *pdwLaunchAddr  = dwRecLen;
                }
            }
            // No TOC - not made by romimage.  However, if we're downloading more than one
            // .bin file, it's probably chain.bin which doesn't have a TOC (and which isn't
            // going to be downloaded on its own) and we should ignore it.
            //
            else if (g_DownloadManifest.dwNumRegions == 1)
                                        
            {
                *pdwImageStart  = pCurDownloadFile->dwRegionStart;
                *pdwImageLength = pCurDownloadFile->dwRegionLength;
                *pdwLaunchAddr  = dwRecLen;
            }
        }
        else    // Raw binary file.
        {
            *pdwImageStart  = pCurDownloadFile->dwRegionStart;
            *pdwLaunchAddr  = pCurDownloadFile->dwRegionStart;
            *pdwImageLength = pCurDownloadFile->dwRegionLength;
        }

        // write to flash if it's flash image
        if (fIsFlash)
        {
            // finish the flash erase
            if (!OEMFinishEraseFlash ())
            {
                HALT (BLERR_FLASH_ERASE);
                return (FALSE);
            }
            // Before writing the image to flash, optionally check the image signature.
            if (g_bBINDownload && g_pOEMCheckSignature)
            {
                if (!g_pOEMCheckSignature(pCurDownloadFile->dwRegionStart, g_dwROMOffset, *pdwLaunchAddr, TRUE))
                    HALT(BLERR_CAT_SIGNATURE);
            }
        }
    }
    while (--nNumDownloadFiles);

	EdbgOutputDebugString("fIsFlash=%d, NumRegions=%d\r\n", fIsFlash, g_DownloadManifest.dwNumRegions);

    if (fIsFlash)
    {
        nNumDownloadFiles = (BYTE)g_DownloadManifest.dwNumRegions;
        while (nNumDownloadFiles--)
        {
            if (!OEMWriteFlash (g_DownloadManifest.Region[nNumDownloadFiles].dwRegionStart, g_DownloadManifest.Region[nNumDownloadFiles].dwRegionLength))
            {
                HALT (BLERR_FLASH_WRITE);
                return (FALSE);
            }
        }
    }

	EdbgOutputDebugString("-DownloadImage\r\n");
    return (TRUE);
}


/****************************************************************************************************
*   DownloadImageSDMMC
*      Downloads an OS image and decodes it if its a binary file
*   Input:
*      pdwImageStart - pointer the the start of the image
*      pwdImageLength - the length of the image
*      pdwLaunchAddr - the OS launch address
*   Output:
*      The image is decoded(if necessary) and maved to the correct memory location
*   Returns:
*      TRUE - if the download was successful
*      FALSE - otherwise
*****************************************************************************************************/
static BOOL DownloadImageSDMMC (LPDWORD pdwImageStart, LPDWORD pdwImageLength, LPDWORD pdwLaunchAddr)
{
    BYTE hdr[BL_HDRSIG_SIZE];
    DWORD dwRecLen, dwRecChk, dwRecAddr;
    BOOL fIsFlash = FALSE;
    LPBYTE lpDest = NULL;
    int nPkgNum = 0;
    BYTE nNumDownloadFiles = 1;
    DWORD dwImageStart = 0;
    DWORD dwImageLength = 0;
    RegionInfo *pCurDownloadFile;
	DWORD offset = 0, romhdr = 0;

    *pdwImageStart = *pdwImageLength = *pdwLaunchAddr = 0;

    do
    {
        // read the 7 byte "magic number"
        //
        if (!SDMMCReadData (BL_HDRSIG_SIZE, hdr, offset))
        {
            EdbgOutputDebugString ("\r\nUnable to read image signature.\r\n");
            HALT (BLERR_MAGIC);
            return (FALSE);
        }

//		EdbgOutputDebugString ("\r\nhdr = %s\r\n", hdr);

		offset+=BL_HDRSIG_SIZE;

        // Is this a standard bin image?  Check for the usual bin file signature.
        //
        if (!memcmp (hdr, "B000FF\x0A", BL_HDRSIG_SIZE))
        {
            g_bBINDownload = TRUE;

            if (!SDMMCReadData (sizeof (DWORD), (LPBYTE) &dwImageStart, offset)
                || !SDMMCReadData (sizeof (DWORD), (LPBYTE) &dwImageLength, offset+=(sizeof (DWORD))))
            {
                EdbgOutputDebugString ("Unable to read image start/length\r\n");
                HALT (BLERR_MAGIC);
                return (FALSE);
            }
			offset+=(sizeof (DWORD));
        }
        // If the header signature isn't recognized, we'll assume the
        // download file is a raw .nb0 file.
        //
        else
            g_bBINDownload = FALSE;

        // If Platform Builder didn't provide a download manifest (i.e., we're
        // only downloading a single .bin file), manufacture a manifest so we
        // can notify the OEM.
        //
        if (!g_DownloadManifest.dwNumRegions)
        {
			EdbgOutputDebugString ("No download manifest!\r\n");
            g_DownloadManifest.dwNumRegions             = 1;
            g_DownloadManifest.Region[0].dwRegionStart  = dwImageStart;
            g_DownloadManifest.Region[0].dwRegionLength = dwImageLength;

            // Provide the download manifest to the OEM.
            //
            if (g_pOEMMultiBINNotify)
            {
                g_pOEMMultiBINNotify((PDownloadManifest)&g_DownloadManifest);
            }
        }

        // Locate the current download manifest entry (current download file).
        //
        pCurDownloadFile = &g_DownloadManifest.Region[g_DownloadManifest.dwNumRegions - nNumDownloadFiles];

        // give the OEM a chance to verify memory
        if (g_pOEMVerifyMemory && !g_pOEMVerifyMemory (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
        {
            EdbgOutputDebugString ("!OEMVERIFYMEMORY: Invalid image\r\n");
            HALT (BLERR_OEMVERIFY);
            return (FALSE);
        }

        // check for flash image. Start erasing if it is.
        if ((fIsFlash = OEMIsFlashAddr (pCurDownloadFile->dwRegionStart)) 
            && !OEMStartEraseFlash (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
        {
            EdbgOutputDebugString ("Invalid Flash Address/Length\r\n");
            HALT (BLERR_FLASHADDR);
            return (FALSE);
        }

        // if we're downloading a binary file, we've already downloaded part of the image when searching
        // for a file header.  copy what we've read so far to the destination buffer, then finish downloading.
        if (!g_bBINDownload)
        {

            lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart);

            // complete the file download...
            // read data block
            if (!SDMMCReadData ((pCurDownloadFile->dwRegionLength - BL_HDRSIG_SIZE), (lpDest + BL_HDRSIG_SIZE), offset))
            {
                EdbgOutputDebugString ("ERROR: failed when reading raw binary file.\r\n");
                HALT (BLERR_CORRUPTED_DATA);
                return (FALSE);
            }

			offset+=(pCurDownloadFile->dwRegionLength - BL_HDRSIG_SIZE);
        }
        // we're downloading a .bin file - download each .bin record in turn...
        else
        {
			EdbgOutputDebugString("DownloadImage: Binary file\r\n");
            while (SDMMCReadData (sizeof (DWORD), (LPBYTE) &dwRecAddr, offset) &&
                   SDMMCReadData (sizeof (DWORD), (LPBYTE) &dwRecLen, offset+=(sizeof(DWORD)))  &&
                   SDMMCReadData (sizeof (DWORD), (LPBYTE) &dwRecChk, offset+=(sizeof(DWORD))))
            {
//                EdbgOutputDebugString("Record: Addr=0x%X, Len=0x%X (%d), Chk=0x%X\r\n", dwRecAddr, dwRecLen, dwRecLen, dwRecChk);
                // last record of .bin file uses sentinel values for address and checksum.
                if (!dwRecAddr && !dwRecChk)
                {
					EdbgOutputDebugString("Download Completed\r\n");
                    break;
                }

                // map the record address (FLASH data is cached, for example)
                lpDest = OEMMapMemAddr (pCurDownloadFile->dwRegionStart, dwRecAddr);

//				EdbgOutputDebugString("Download to 0x%x\r\n", lpDest);

                // read data block
                if (!(SDMMCReadData (dwRecLen, lpDest, offset+=(sizeof(DWORD)))))
                {
                    EdbgOutputDebugString ("****** Data record %d corrupted, ABORT!!! ******\r\n", nPkgNum);
                    HALT (BLERR_CORRUPTED_DATA);
                    return (FALSE);
                }

				offset+=dwRecLen;

                if (!VerifyChecksum (dwRecLen, lpDest, dwRecChk))
                {
                    EdbgOutputDebugString ("****** Checksum failure on record %d, ABORT!!! ******\r\n", nPkgNum);
                    HALT (BLERR_CHECKSUM);
                    return (FALSE);
                }

                // Look for ROMHDR to compute ROM offset.  NOTE: romimage guarantees that the record containing
                // the TOC signature and pointer will always come before the record that contains the ROMHDR contents.
                //
                if (dwRecLen == sizeof(ROMHDR) && ((*(LPDWORD) OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)))
                {
                    DWORD dwTempOffset = (dwRecAddr - *(LPDWORD)OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG)));
                    ROMHDR *pROMHdr = (ROMHDR *)lpDest;

					EdbgOutputDebugString("*** Potential ROMHDR at %x\r\n", lpDest);
					//EdbgOutputDebugString("*** pROMHdr->physfirst 0x%X\r\n", pROMHdr->physfirst);
					//EdbgOutputDebugString("*** pROMHdr->physlast 0x%X\r\n", pROMHdr->physlast);
                    // Check to make sure this record really contains the ROMHDR.
                    //
                    if ((pROMHdr->physfirst == (pCurDownloadFile->dwRegionStart - dwTempOffset)) &&
                        (pROMHdr->physlast  == (pCurDownloadFile->dwRegionStart - dwTempOffset + pCurDownloadFile->dwRegionLength)) &&
                        (DWORD)(HIWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast) &&
                        (DWORD)(LOWORD(pROMHdr->dllfirst << 16) <= pROMHdr->dlllast))
                    {
                        g_dwROMOffset = dwTempOffset;
                        EdbgOutputDebugString("rom_offset=0x%x.\r\n", g_dwROMOffset); 
                    }
                }

                // verify partial checksum
                OEMShowProgress (nPkgNum ++);

                if (fIsFlash)
                {
                    OEMContinueEraseFlash ();
                }
            }
        }

		//EdbgOutputDebugString("Process data\r\n");
        // The image start address and length are passed back to the OEM code (OEMLaunch) 
        // in the following circumstances:
        // 1. The file is a raw .nb0 file.
        // 2. The file is a .bin file with a TOC that contains the kernel executable.
        // 3. The file is a .bin file without a TOC.
        //
        // If the image is a .bin file with a TOC that doesn't contain the kernel exectuable,
        // then it's a multi-xip/mulit-bin image for a non-kernel region and we don't pass
        // the start address and length back to the OEM code.  OEMLaunch can then save the
        // start address and length with the assurance that if the values are non-zero, they
        // represent the values for the NK region.
        //
        if (g_bBINDownload)
        {
//			EdbgOutputDebugString("Binary information: RegionStart=0x%x, RegionLength=0x%x\r\n", pCurDownloadFile->dwRegionStart,
//				pCurDownloadFile->dwRegionLength);
//			EdbgOutputDebugString("Suppose now access violation\r\n");
//			EdbgOutputDebugString("Accessing data = 0x%x\r\n", *(UINT32 *)(pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET));
            // Does this .bin file contain a TOC?
            if (*(LPDWORD) OEMMapMemAddr(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionStart + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
            {	
				EdbgOutputDebugString("Access OKAY\r\n");
                // Contain the kernel?
                if (IsKernelRegion(pCurDownloadFile->dwRegionStart, pCurDownloadFile->dwRegionLength))
                {
                    *pdwImageStart  = pCurDownloadFile->dwRegionStart;
                    *pdwImageLength = pCurDownloadFile->dwRegionLength;
                    *pdwLaunchAddr  = dwRecLen;
                }
            }
            // No TOC - not made by romimage.  However, if we're downloading more than one
            // .bin file, it's probably chain.bin which doesn't have a TOC (and which isn't
            // going to be downloaded on its own) and we should ignore it.
            //
            else if (g_DownloadManifest.dwNumRegions == 1)
                                        
            {
                *pdwImageStart  = pCurDownloadFile->dwRegionStart;
                *pdwImageLength = pCurDownloadFile->dwRegionLength;
                *pdwLaunchAddr  = dwRecLen;
            }
        }
        else    // Raw binary file.
        {
            *pdwImageStart  = pCurDownloadFile->dwRegionStart;
            *pdwLaunchAddr  = pCurDownloadFile->dwRegionStart;
            *pdwImageLength = pCurDownloadFile->dwRegionLength;
        }

        // write to flash if it's flash image
        if (fIsFlash)
        {
            // finish the flash erase
            if (!OEMFinishEraseFlash ())
            {
                HALT (BLERR_FLASH_ERASE);
                return (FALSE);
            }
            // Before writing the image to flash, optionally check the image signature.
            if (g_bBINDownload && g_pOEMCheckSignature)
            {
                if (!g_pOEMCheckSignature(pCurDownloadFile->dwRegionStart, g_dwROMOffset, *pdwLaunchAddr, TRUE))
                    HALT(BLERR_CAT_SIGNATURE);
            }
        }
    }
    while (--nNumDownloadFiles);

	EdbgOutputDebugString("fIsFlash=%d, NumRegions=%d\r\n", fIsFlash, g_DownloadManifest.dwNumRegions);

    if (fIsFlash)
    {
        nNumDownloadFiles = (BYTE)g_DownloadManifest.dwNumRegions;
        while (nNumDownloadFiles--)
        {	
            if (!OEMWriteFlash (g_DownloadManifest.Region[nNumDownloadFiles].dwRegionStart, g_DownloadManifest.Region[nNumDownloadFiles].dwRegionLength))
            {
                HALT (BLERR_FLASH_WRITE);
                return (FALSE);
            }
			
        }
    }

	EdbgOutputDebugString("-DownloadImage\r\n");
    return (TRUE);
}


/*
    @func   BOOL | IsKernelRegion | Determines if the expanded BIN file provided contains the kernel image.
    @rdesc  TRUE if the region contains the kernel image, FALSE if it doesn't.
    @comm   <l Download Image> 
    @xref   
    @notes  dwCurrentBase is the base address where the BIN records are currently stored (this can be a RAM, a RAM
            file cache, or flash).  dwImageStoreBase is the images base storage address (this is the base address of potentially
            multiple BIN regions and can be in RAM or flash) and is used to translate addresses to the file cache area.
            dwROMOffset is the difference between the address where the BIN records are stored versus where they're fixed-up
            to run from (for example, an image may be stored in flash, but fixed-up to run in RAM).
*/
static BOOL IsKernelRegion(DWORD dwRegionStart, DWORD dwRegionLength)
{
	DWORD dwCacheAddress = 0;
	ROMHDR *pROMHeader;
	DWORD dwNumModules = 0;
	TOCentry *plTOC;

    if (dwRegionStart == 0 || dwRegionLength == 0)
        return(FALSE);

    if (*(LPDWORD) OEMMapMemAddr (dwRegionStart, dwRegionStart + ROM_SIGNATURE_OFFSET) != ROM_SIGNATURE)
        return (FALSE);

    // A pointer to the ROMHDR structure lives just past the ROM_SIGNATURE (which is a longword value).  Note that
    // this pointer is remapped since it might be a flash address (image destined for flash), but is actually cached
    // in RAM.
    //
    dwCacheAddress = *(LPDWORD) OEMMapMemAddr (dwRegionStart, dwRegionStart + ROM_SIGNATURE_OFFSET + sizeof(ULONG));
    pROMHeader     = (ROMHDR *) OEMMapMemAddr (dwRegionStart, dwCacheAddress + g_dwROMOffset);

    // Make sure sure are some modules in the table of contents.
    //
    if ((dwNumModules = pROMHeader->nummods) == 0)
        return (FALSE);

	// Locate the table of contents and search for the kernel executable and the TOC immediately follows the ROMHDR.
	//
    plTOC = (TOCentry *)(pROMHeader + 1);

	while(dwNumModules--) {
		LPBYTE pFileName = OEMMapMemAddr(dwRegionStart, (DWORD)plTOC->lpszFileName + g_dwROMOffset);
		if (!strcmp(pFileName, "nk.exe")) {
			return TRUE;
		}
		++plTOC;
	}
	return FALSE;
}


VOID DumpTOC(ROMHDR *pTOC)
{
    // Print out ROMHDR information
    EdbgOutputDebugString ("\r\n");
    EdbgOutputDebugString ("ROMHDR (pTOC = 0x%x) ---------------------\r\n", pTOC);
    EdbgOutputDebugString ("  DLL First           : 0x%x\r\n", pTOC->dllfirst);
    EdbgOutputDebugString ("  DLL Last            : 0x%x\r\n", pTOC->dlllast);
    EdbgOutputDebugString ("  Physical First      : 0x%x\r\n", pTOC->physfirst);
    EdbgOutputDebugString ("  Physical Last       : 0x%x\r\n", pTOC->physlast);
    EdbgOutputDebugString ("  Num Modules         : %d\r\n",   pTOC->nummods);
    EdbgOutputDebugString ("  RAM Start           : 0x%x\r\n", pTOC->ulRAMStart);
    EdbgOutputDebugString ("  RAM Free            : 0x%x\r\n", pTOC->ulRAMFree);
    EdbgOutputDebugString ("  RAM End             : 0x%x\r\n", pTOC->ulRAMEnd);
    EdbgOutputDebugString ("  Num Copy Entries    : %d\r\n",   pTOC->ulCopyEntries);
    EdbgOutputDebugString ("  Copy Entries Offset : 0x%x\r\n", pTOC->ulCopyOffset);
    EdbgOutputDebugString ("  Prof Symbol Length  : 0x%x\r\n", pTOC->ulProfileLen);
    EdbgOutputDebugString ("  Prof Symbol Offset  : 0x%x\r\n", pTOC->ulProfileOffset);
    EdbgOutputDebugString ("  Num Files           : %d\r\n",   pTOC->numfiles);
    EdbgOutputDebugString ("  Kernel Flags        : 0x%x\r\n", pTOC->ulKernelFlags);
    EdbgOutputDebugString ("  FileSys RAM Percent : 0x%x\r\n", pTOC->ulFSRamPercent);
    EdbgOutputDebugString ("  Driver Glob Start   : 0x%x\r\n", pTOC->ulDrivglobStart);
    EdbgOutputDebugString ("  Driver Glob Length  : 0x%x\r\n", pTOC->ulDrivglobLen);
    EdbgOutputDebugString ("  CPU                 : 0x%x\r\n", pTOC->usCPUType);
    EdbgOutputDebugString ("  MiscFlags           : 0x%x\r\n", pTOC->usMiscFlags);
    EdbgOutputDebugString ("  Extensions          : 0x%x\r\n", pTOC->pExtensions);
    EdbgOutputDebugString ("  Tracking Mem Start  : 0x%x\r\n", pTOC->ulTrackingStart);
    EdbgOutputDebugString ("  Tracking Mem Length : 0x%x\r\n", pTOC->ulTrackingLen);
    EdbgOutputDebugString ("\r\n\r\n");
}
